package engine.groups
{
	import ash.core.Entity;
	import ash.core.System;
	
	import engine.GameEngine;
	import engine.managers.FileManager;
	import engine.systems.group.Children;
	import engine.systems.group.Parent;
	import engine.utils.ClassUtils;
	import engine.utils.Command;
	
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;
	
	import org.osflash.signals.Signal;

	public class Group
	{
		public var files:Array 		= new Array();
		
		public var ready:Signal 	= new Signal(Group);
		public var removed:Signal 	= new Signal(Group);
		
		private const _name:String 		= getQualifiedClassName(this).split("::")[1];
		private const _filePath:String 	= getQualifiedClassName(this).split("::")[0].replace(/\./g, "/") + "/";
		
		private var _paused:Boolean = false;
		
		internal var _parent:Group;
		internal var _children:Dictionary 	= new Dictionary(true);
		internal var _entities:Dictionary 	= new Dictionary(true);
		internal var _systems:Dictionary 	= new Dictionary(true);
		
		public function Group(parent:Group)
		{
			this.parent = parent;
			
			GameEngine.groupManager.add(this);
		}
		
		public function destroy():void
		{
			for each(var entity:Entity in this._entities) 	this.removeEntity(entity, false);
			for each(var system:System in this._systems) 	this.removeSystem(system, false);
			for each(var child:Group in this._children) 	this.removeGroup(child, false);
			if(this._parent) 								delete this._parent._children[this._name];
			
			this._entities 	= null;
			this._systems 	= null;
			this._children 	= null;
			this._parent 	= null;
			
			this.removed.dispatch(this);
			
			this.ready.removeAll();
			this.ready = null;
			
			this.removed.removeAll();
			this.removed = null;
		}
		
		public final function get name():String 	{ return this._name.slice(); }
		public final function get filePath():String { return this._filePath.slice(); }
		
		public final function get children():Vector.<Group>
		{
			var children:Vector.<Group> = new Vector.<Group>();
			for each(var child:Group in this._children)
			{
				children.push(child);
			}
			return children;
		}
		
		public final function get entities():Vector.<Entity>
		{
			var entities:Vector.<Entity> = new Vector.<Entity>();
			for each(var entity:Entity in this._entities)
			{
				entities.push(entity);
			}
			return entities;
		}
		
		public final function get systems():Vector.<System>
		{
			var systems:Vector.<System> = new Vector.<System>();
			for each(var system:System in this._systems)
			{
				systems.push(system);
			}
			return systems;
		}
		
		public final function get paused():Boolean { return this._paused; }
		public final function pause(pause:Boolean, pauseChildren:Boolean = true):void
		{
			this._paused = pause;
			
			if(!pauseChildren) return;
			
			for each(var child:Group in this._children)
			{
				child.pause(pause, pauseChildren);
			}
		}
		
		public final function get parent():Group { return this._parent; }
		public final function set parent(parent:Group):void
		{
			if(this._parent == parent) return;
			
			if(this._parent) delete this._parent._children[this._name];
			
			this._parent = parent;
			if(this._parent) this._parent._children[this._name] = this;
		}
		
		public final function removeGroup(group:Group, completeUpdates:Boolean = true):Boolean
		{
			if(!group) return false;
			
			if(!GameEngine.groupManager.getByName(group.name)) return false;
			
			if(completeUpdates)
			{
				GameEngine.systemManager.updateComplete.addOnce(Command.create(this.removeGroup, group, false));
			}
			else
			{
				GameEngine.groupManager.remove(group);
			}
			
			return true;
		}
		
		public function load():void
		{
			if(this.files.length > 0)
				this.loadFiles(files, this.loaded);
				
			else this.loaded();
		}
		
		public function loaded():void
		{
			/**
			 * This really shouldn't be here. It should be getting dispatched at the top-most level.
			 */
			this.ready.dispatch(this);
		}
		
		private final function getGroupFilePath(file:String):String
		{
			var prefix:String = "";
			
			if(String(file).indexOf(".xml") > -1) prefix = FileManager.DATA_PATH;
			else prefix = FileManager.ASSET_PATH;
			
			return prefix + this._filePath + file;
		}
		
		public final function getFile(file:String, clear:Boolean = true):*
		{
			file = this.getGroupFilePath(file);
			return GameEngine.fileManager.getFile(file, clear);
		}
		
		public final function loadFile(file:String, handler:Function):void
		{
			file = this.getGroupFilePath(file);
			GameEngine.fileManager.loadFile(file, handler);
		}
		
		public final function loadFiles(files:Array, handler:Function):void
		{
			if(!files || files.length <= 0) return;
			
			for(var i:int = 0; i < files.length; i++)
				files[i] = this.getGroupFilePath(files[i]);
			
			GameEngine.fileManager.loadFiles(files, handler);
		}
		
		public final function getGroupByName(name:String):Group
		{
			return GameEngine.groupManager.getByName(name);
		}
		
		public final function getGroupByClass(groupClass:Class):Group
		{
			return GameEngine.groupManager.getByClass(groupClass);
		}
		
		public final function getEntityByName(name:String):Entity
		{
			return GameEngine.systemManager.getEntityByName(name);
		}
		
		public final function addEntity(entity:Entity):Boolean
		{
			if(!entity) return false;
			
			if(GameEngine.systemManager.getEntityByName(entity.name)) return false;
			
			entity.group = this;
			this._entities[entity] = entity;
			
			GameEngine.systemManager.addEntity(entity);
			
			return true;
		}	

		public final function removeEntity(entity:Entity, completeUpdates:Boolean = true):Boolean
		{
			if(!entity) return false;
			
			if(!GameEngine.systemManager.getEntityByName(entity.name)) return false;
			
			if(completeUpdates)
			{
				GameEngine.systemManager.updateComplete.addOnce(Command.create(this.removeEntity, entity, false));
			}
			else
			{
				var children:Children = entity.get(Children);
				if(children)
				{
					while(children.children.length > 0)
						this.removeEntity(children.children.pop(), false);
				}
				
				var parent:Parent = entity.get(Parent);
				if(parent)
				{
					children = parent.parent.get(Children);
					if(children)
					{
						for(var i:int = 0; i < children.children.length; i++)
						{
							if(children.children[i] == entity)
							{
								children.children.splice(i, 1);
								break;
							}
						}
					}
				}
				
				delete this._entities[entity];
				entity.group = null;
				
				GameEngine.systemManager.removeEntity(entity);
			}
			
			return true;
		}
		
		public final function getSystem(systemClass:Class):System
		{
			if(!systemClass) return null;
			
			return GameEngine.systemManager.getSystem(systemClass);
		}
		
		public final function addSystem(system:System):Boolean
		{
			if(!system) return false;
			
			var systemClass:Class = ClassUtils.toClass(system);
			if(GameEngine.systemManager.getSystem(systemClass)) return false;
			
			system.group = this;
			this._systems[system] = system;
			
			GameEngine.systemManager.addSystem(system);
			
			return true;
		}
		
		public final function removeSystem(system:System, completeUpdates:Boolean = true):Boolean
		{
			if(!system) return false;
			
			if(!GameEngine.systemManager.getSystem(ClassUtils.toClass(system))) return false;
			
			if(completeUpdates)
			{
				GameEngine.systemManager.updateComplete.addOnce(Command.create(this.removeSystem, system, false));
			}
			else
			{
				delete this._systems[system];
				system.group = null;
				
				GameEngine.systemManager.removeSystem(system);
			}
			
			return true;
		}
		
		
	}
}